﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using HFUTCourseSimulation.Util;

namespace HFUTCourseSimulation.Kernel {

    /// <summary>
    /// 渲染器的图片的几何形状
    /// 
    /// 渲染后的图片中，教学周从左到右从上到下排布。
    /// 整个图片的长和宽上的教学周数量，要使得最终的图形尽量为正方形。
    /// 每个教学周外围有MARGIN的边框留白。
    /// 教学周中，最上方是Title，书写当前的教学周号。
    /// 然后是表格，表格上有Headbar显示星期几和日期，左有Sidebar显示节次。
    /// 最后是内中表格。
    /// </summary>
    internal class RenderGeometry {
        /// <summary>
        /// 教学周标题所占的高度
        /// </summary>
        public static readonly int TITLE_HEIGHT = 50;
        /// <summary>
        /// 表头（星期几和日期）所占的高度
        /// </summary>
        public static readonly int HEADBAR_HEIGHT = 40;
        /// <summary>
        /// 侧边（节次号）所占的高度
        /// </summary>
        public static readonly int SIDEBAR_WIDTH = 25;
        /// <summary>
        /// 每个节次Cell的宽度
        /// </summary>
        public static readonly int CELL_WIDTH = 150;
        /// <summary>
        /// 每个节次Cell的高度
        /// </summary>
        public static readonly int CELL_HEIGHT = 80;
        /// <summary>
        /// 每个表格之间的长宽上的间距
        /// </summary>
        public static readonly int MARGIN = 20;

        public RenderGeometry(int weekCount, int indexCount) {
            _horizontalCount = (int)Math.Sqrt(weekCount) + 1;
            _verticalCount = weekCount / _horizontalCount;
            if (weekCount % _horizontalCount != 0) _verticalCount++;

            _weekWidth = SIDEBAR_WIDTH + 7 * CELL_WIDTH;
            _weekHeight = TITLE_HEIGHT + HEADBAR_HEIGHT + indexCount * CELL_HEIGHT;

            _weekWidthWithMargin = _weekWidth + 2 * MARGIN;
            _weekHeightWithMargin = _weekHeight + 2 * MARGIN;

            _width = _weekWidthWithMargin * _horizontalCount;
            _height = _weekHeightWithMargin * _verticalCount;
        }

        int _horizontalCount, _verticalCount;
        int _weekWidth, _weekHeight;
        int _weekWidthWithMargin, _weekHeightWithMargin;
        int _width, _height;

        /// <summary>
        /// 图片宽度
        /// </summary>
        public int Width { get { return _width; } }
        /// <summary>
        /// 图片高度
        /// </summary>
        public int Height { get { return _height; } }

        /// <summary>
        /// 获取教学周的左上角坐标（实际开始画图的开始，排除Margin）
        /// </summary>
        private Point GetWeekPos(int week) {
            week -= 1;

            var row = week / _horizontalCount;
            var column = week % _horizontalCount;
            return new Point(column * _weekWidthWithMargin + MARGIN, row * _weekHeightWithMargin + MARGIN);
        }

        public Rectangle GetTitleTextRect(int week) {
            var pos = GetWeekPos(week);
            return new Rectangle(pos, new Size(_weekWidth, TITLE_HEIGHT));
        }

        /// <summary>
        /// 获取课表主体的左上角的坐标
        /// </summary>
        private Point GetTablePos(int week) {
            var pos = GetWeekPos(week);
            pos.Offset(0, TITLE_HEIGHT);
            return pos;
        }

        public Rectangle GetHeadbarTextRect(int week, int day) {
            day -= 1;

            var pos = GetTablePos(week);
            pos.Offset(SIDEBAR_WIDTH + day * CELL_WIDTH, 0);
            return new Rectangle(pos, new Size(CELL_WIDTH, HEADBAR_HEIGHT));
        }

        public Rectangle GetHeadbarBoxRect(int week) {
            var pos = GetTablePos(week);
            return new Rectangle(pos, new Size(SIDEBAR_WIDTH + 7 * CELL_WIDTH, HEADBAR_HEIGHT));
        }

        public Rectangle GetSidebarTextRect(int week, int index) {
            return GetSidebarBoxRect(week, index);
        }

        public Rectangle GetSidebarBoxRect(int week, int index) {
            index -= 1;

            var pos = GetTablePos(week);
            pos.Offset(0, HEADBAR_HEIGHT + index * CELL_HEIGHT);
            return new Rectangle(pos, new Size(SIDEBAR_WIDTH, CELL_HEIGHT));
        }

        /// <summary>
        /// 获取课表课程区域的左上角坐标（排除顶栏和侧栏）
        /// </summary>
        private Point GetBodyPos(int week) {
            var pos = GetTablePos(week);
            pos.Offset(SIDEBAR_WIDTH, HEADBAR_HEIGHT);
            return pos;
        }

        /// <summary>
        /// 获取指定周，指定星期，指定节次的表格的左上角坐标
        /// </summary>
        private Point GetCellPos(int week, int day, int index) {
            day -= 1;
            index -= 1;

            var pos = GetBodyPos(week);
            pos.Offset(day * CELL_WIDTH, index * CELL_HEIGHT);
            return pos;
        }

        public Rectangle GetCellTextRect(int week, int day, int index, int span = 1) {
            return GetCellBoxRect(week, day, index, span);
        }

        public Rectangle GetCellBoxRect(int week, int day, int index, int span = 1) {
            var pos = GetCellPos(week, day, index);
            return new Rectangle(pos, new Size(CELL_WIDTH, span * CELL_HEIGHT));
        }

    }

    /// <summary>
    /// 渲染器笔刷集合。
    /// 渲染器工作时，需要频繁创建各种不同颜色的笔刷，此处为其缓存这些笔刷。
    /// </summary>
    public class RenderBrushes {
        public RenderBrushes() {
            _brushes = new Dictionary<Color, SolidBrush>();
        }

        public SolidBrush GetBrush(Color color) {
            if (_brushes.TryGetValue(color, out SolidBrush brush)) {
                return brush;
            } else {
                var new_brush = new SolidBrush(color);
                _brushes.Add(color, new_brush);
                return new_brush;
            }
        }

        public SolidBrush GetBrush(System.Windows.Media.Color color) {
            return GetBrush(ColorTrans.ToGdiColor(color));
        }

        Dictionary<Color, SolidBrush> _brushes;
    }

    /// <summary>
    /// 核心图片渲染器
    /// </summary>
    public static class Render {

        /// <summary>
        /// 渲染给定课表到给定图片中。
        /// </summary>
        /// <param name="semester">要渲染的课表</param>
        /// <param name="filepath">要渲染到的图片路径</param>
        /// <returns>如果无误，返回true，否则返回false。</returns>
        public static bool Rending(Kernel.Data.Presentation.Semester semester, string filepath) {
            try {
                UnderlyingRending(semester, filepath);
                return true;
            } catch (Exception) {
                return false;
            }
        }

        private static void UnderlyingRending(Kernel.Data.Presentation.Semester semester, string filepath) {
            // 确定画布基本属性
            var geometry = new RenderGeometry(semester.weekCount, semester.indexCount);

            // 创建图像
            using (var img = new Bitmap(geometry.Width, geometry.Height)) {
                // 创建画布
                Graphics g = Graphics.FromImage(img);

                // 创建绘画资源
                var brushes = new RenderBrushes();
                var font = new Font("Sarasa Mono SC", 12);

                // 填充背景为白色
                g.Clear(ColorTrans.ToGdiColor(ColorConsistency.Backboard.Background));

                // 按教学周周分别输出
                for (int week = 1; week <= semester.weekCount; ++week) {
                    var week_instance = semester.weeks[week - 1];

                    // 教学周文本
                    g.DrawString($"教学周：{week}", font, brushes.GetBrush(ColorConsistency.Backboard.Foreground), geometry.GetTitleTextRect(week));

                    // 绘制Headbar底层
                    g.FillRectangle(brushes.GetBrush(ColorConsistency.HeadbarColor.Background), geometry.GetHeadbarBoxRect(week));
                    // 绘制Header文本
                    for (int day = 1; day <= 7; ++day) {
                        var day_instance = week_instance.days[day - 1];
                        g.DrawString($@"{WeekNames.Names[day - 1]}
{day_instance.date}", font, brushes.GetBrush(ColorConsistency.HeadbarColor.Foreground), geometry.GetHeadbarTextRect(week, day));
                    }

                    // 绘制Sidebar底层和文本
                    for (int index = 1; index <= semester.indexCount; ++index) {
                        var colorPair = ColorConsistency.GetSidebarColor(semester.GetIndexKind(index));
                        g.FillRectangle(brushes.GetBrush(colorPair.Background), geometry.GetSidebarBoxRect(week, index));
                        g.DrawString(index.ToString(), font, brushes.GetBrush(colorPair.Foreground), geometry.GetSidebarTextRect(week, index));
                    }

                    // 绘制Chessboard
                    for (int day = 1; day <= 7; ++day) {
                        for (int index = 1; index <= semester.indexCount; ++index) {
                            if ((day + index) % 2 == 0) continue;
                            g.FillRectangle(brushes.GetBrush(ColorConsistency.ChessboardColor), geometry.GetCellBoxRect(week, day, index));
                        }
                    }

                    // 绘制课程
                    for (int day = 1; day <= 7; ++day) {
                        var day_instance = week_instance.days[day - 1];
                        foreach (var lesson_instance in day_instance.lessons) {
                            g.FillRectangle(brushes.GetBrush(lesson_instance.color.Background), geometry.GetCellBoxRect(week, day, lesson_instance.startIndex, lesson_instance.indexSpan));
                            g.DrawString($@"{lesson_instance.name}
{lesson_instance.description}", font, brushes.GetBrush(lesson_instance.color.Foreground), geometry.GetCellTextRect(week, day, lesson_instance.startIndex, lesson_instance.indexSpan));
                        }
                    }
                }

                // 保存到文件
                using (var fs = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)) {
                    img.Save(fs, ImageFormat.Png);
                }
            }
        }
    }
}
